package net.wicp.tams.common;

import java.io.File;
import java.io.FileFilter;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import lombok.extern.slf4j.Slf4j;
import net.wicp.tams.common.apiext.CollectionUtil;
import net.wicp.tams.common.apiext.IOUtil;
import net.wicp.tams.common.constant.Encoding;
import net.wicp.tams.common.constant.PathType;
import net.wicp.tams.common.constant.StrPattern;

/**
 * 
 * @author 偏锋书生
 *
 */
@Slf4j
public final class Conf {
	private static final Properties props = new Properties();// IOUtil.fileToProperties("/common-apiext.properties",
																// Conf.class);//
																// 属性配置
	private static final Map<String[], CallbackUpdate> reshBacks = new HashMap<>();// 重新加载配置文件时需要的回调函数,key：关注的属性值组

	private static final String[] firstReads = new String[] { "common-apiext.properties", "common-connector.properties",
			"common-hadoop.properties", "common-http.properties", "common-micro.properties",
			"common-others.properties" };

	static {
		// ScheduledExecutorService service = Executors
		// .newSingleThreadScheduledExecutor();
		// 第二个参数为首次执行的延时时间，第三个参数为定时执行的间隔时间
		// service.scheduleAtFixedRate(runnable, 10, 1, TimeUnit.SECONDS);
		// 加文件监听
		String properPattern = "^(BOOT-INF/classes/)?common-([a-zA-z0-9]-*){1,}\\.properties$";
		FileAlterationMonitor monitor = new FileAlterationMonitor(1000L);// 每隔1000毫秒扫描一次
		FileFilter fileFilter = new RegexFileFilter(properPattern);
		FileAlterationObserver observer = new FileAlterationObserver(
				new File(PathType.getPath(get("common.apiext.checkpath"))), fileFilter);
		FileListerAdapter listener = new FileListerAdapter();
		observer.addListener(listener);
		monitor.addObserver(observer);
		try {
			monitor.start();
		} catch (Exception e) {
			log.error("文件监控错误", e);
		}
		List<File> userConfs = new ArrayList<>();
		// 加载common-*子项目的属性文件
		List<URL> roots = IOUtil.findHasPackRootPath("net.wicp.tams");
		for (URL root : roots) {
			if ("jar".equals(root.getProtocol())) {
				try {
					Map<String, InputStream> is = IOUtil.getFilesFromJar(root.getPath(), ".", "properties");
					String fileDir = IOUtil.getCurFolder(Conf.class, false).getPath();
					File dir = new File(IOUtil.mergeFolderAndFilePath(fileDir, "/temp"));
					if (!dir.exists()) {
						dir.mkdir();
					}
					for (String key : is.keySet()) {
						if (key.contains("/common-") && key.contains(".jar")) {
							// log.info("commjar:{}",key);
							String[] paths = key.split("/");
							File newFile = new File(dir, paths[paths.length - 1]);
							log.info("newfile:{}", newFile.getPath());
							if (!newFile.exists()) {
								FileUtils.copyInputStreamToFile(is.get(key), newFile);
							}
							String filepathTrue = newFile.getPath().startsWith("file:") ? "jar:" + newFile.getPath()
									: "jar:file:" + newFile.getPath();
							// log.info("------------proppath:{}----------",filepathTrue);
							Map<String, InputStream> map2 = IOUtil.getFilesFromJar(filepathTrue, ".", "properties");
							for (String key2 : map2.keySet()) {
								if (StrPattern.checkStrFormat(properPattern, key2)) {
									Properties returnPro = new Properties();
									returnPro.load(map2.get(key2));
									overProp(returnPro);
								}
							}
						} else if (StrPattern.checkStrFormat(properPattern, key)) {
							Properties returnPro = new Properties();
							returnPro.load(is.get(key));
							overProp(returnPro);
						}
					}
				} catch (Exception e) {
					log.error("加载properties错误", e);
				}

			} else if ("file".equals(root.getProtocol())) {
				File dir = new File(root.getPath());
				File[] files = dir.listFiles(fileFilter);
				for (File file : files) {
					if (!ArrayUtils.contains(firstReads, file.getName())) {
						userConfs.add(file);
						continue;
					}
					Properties properties = IOUtil.fileToProperties(file);
					overProp(properties);
				}
			}
		}
		for (File file : userConfs) {
			Properties properties = IOUtil.fileToProperties(file);
			overProp(properties);
		}
		log.info("confpropsize:" + props.size());
		for (Object key : props.keySet()) {
			String keystr = String.valueOf(key);
			// System.out.println("key======" + keystr);
			System.setProperty(keystr, props.getProperty(keystr));
		}
	}

	private static class FileListerAdapter extends FileAlterationListenerAdaptor {
		@SuppressWarnings("unchecked")
		@Override
		public void onFileChange(File file) {
			final Properties prop = IOUtil.fileToProperties(file);
			List<String[]> selSet = (List<String[]>) CollectionUtil.selectFilter(reshBacks.keySet(), new Predicate() {
				@Override
				public boolean evaluate(Object object) {
					String[] tempary = (String[]) object;
					for (Object obj : prop.keySet()) {
						if (ArrayUtils.contains(tempary, obj)) {
							return true;
						}
					}
					return false;
				}
			});
			for (String[] sel : selSet) {
				reshBacks.get(sel).doReshConf(props, prop);
			}
			Conf.overProp(prop);
		}

		@Override
		public void onFileCreate(File file) {
			final Properties prop = IOUtil.fileToProperties(file);
			Conf.overProp(prop);
		}

	}

	// 默认区域
	private static Locale curLocale = new Locale(get("common.apiext.i18n") == null ? "zh" : get("common.apiext.i18n"));

	@SuppressWarnings("rawtypes")
	public static void addFileFromJar(String propPath, Class classz) {
		Properties newprops = IOUtil.fileToProperties(propPath, classz);
		overProp(newprops);
	}

	public static void addCallBack(CallbackUpdate callbackupdate, String... proNames) {
		Validate.isTrue(ArrayUtils.isNotEmpty(proNames), "必须传要要检查变更的属性值");
		reshBacks.put(proNames, callbackupdate);
	}

	public static void addCallBack(CallbackUpdate callbackupdate, String pre) {
		Validate.isTrue(StringUtils.isNotBlank(pre));
		Map<String, String> preprop = getPre(pre);
		Validate.isTrue(MapUtils.isNotEmpty(preprop), "变量没有检查到，请检查[" + pre + "]开始的属性是否有配置");
		addCallBack(callbackupdate, preprop.keySet().toArray(new String[preprop.size()]));
	}

	/***
	 * 通过key得到对应的值
	 * 
	 * @param key
	 *            key值
	 * @return 对应的值
	 */
	public static String get(String key) {
		return props.get(key) == null ? null : String.valueOf(props.get(key));
	}

	/***
	 * 得到指定前缀的所有key及他们对应的值
	 * 
	 * @param key
	 *            key的前缀
	 * @return 符合条件的结果集
	 */
	public static Map<String, String> getPre(String key) {
		return CollectionUtil.getPropsByKeypre(props, key);
	}

	/***
	 * 设置当前的Locale
	 * 
	 * @param curLocale
	 *            要设置的Locale
	 */
	public static void setCurLocale(Locale curLocale) {
		if (curLocale != null) {
			Conf.curLocale = curLocale;
		}
	}

	/***
	 * 得到当前的Locale
	 * 
	 * @return 当前的Locale
	 */
	public static Locale getCurLocale() {
		return curLocale;
	}

	/***
	 * 得到配置文件的副本，防止配置文件的属性被窜改
	 * 
	 * @return 属性的克隆
	 */
	public static Properties copyProperties() {
		return (Properties) props.clone();
	}

	/***
	 * 添加或者覆盖配置
	 * 
	 * @param initProperties
	 *            要覆盖的属性
	 */
	public static void overProp(Properties initProperties) {
		if (initProperties != null && initProperties.size() > 0) {
			for (Object keyobj : initProperties.keySet()) {
				String keystr = String.valueOf(keyobj);
				props.put(keyobj, initProperties.get(keyobj));
				System.setProperty(keystr, props.getProperty(keystr));
			}
		}
	}

	/***
	 * 得到操作系统编码
	 * 
	 * @return 操作系统编码
	 */
	public static Encoding getSystemEncode() {
		return Encoding.getByName(System.getProperty("sun.jnu.encoding"));
	}

	/***
	 * 当前运行java的程序入口 (main方法)所在类主类文件的编码
	 * 
	 * @return 主类文件的编码
	 */
	public static Encoding getFileEncode() {
		return Encoding.getByName(System.getProperty("file.encoding"));
	}

}
